import os
import time
import json
import logging
import threading
from flask import Flask, request, jsonify
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    filename='sync_master.log'
)

# 主服务器配置
class Config:
    # 监控目录
    WATCH_DIRS = [
        '/www/wwwroot/',        # 网站根目录
        '/www/server/panel/vhost/nginx/',  # Nginx配置
        '/www/server/panel/config/'        # 宝塔配置
    ]
    # API端口
    API_PORT = 5000
    # 认证密钥
    AUTH_KEY = 'your_secure_api_key'
    # 指令存储文件
    COMMAND_FILE = 'sync_commands.json'

# 事件处理器
class BtConfigHandler(FileSystemEventHandler):
    def on_created(self, event):
        if not event.is_directory:
            self._handle_event('create', event.src_path)

    def on_modified(self, event):
        if not event.is_directory:
            self._handle_event('modify', event.src_path)

    def on_deleted(self, event):
        self._handle_event('delete', event.src_path)

    def _handle_event(self, action, path):
        # 过滤临时文件
        if path.endswith(('.swp', '.tmp', '.bak')):
            return
        
        # 生成指令
        command = {
            'action': action,
            'path': path,
            'timestamp': time.time(),
            'md5': self._calculate_md5(path) if action != 'delete' else None
        }
        
        # 保存指令到文件
        self._save_command(command)
        logging.info(f"记录指令: {action} {path}")

    def _calculate_md5(self, file_path):
        # 计算文件MD5值
        import hashlib
        if not os.path.exists(file_path):
            return None
        try:
            with open(file_path, 'rb') as f:
                return hashlib.md5(f.read()).hexdigest()
        except Exception as e:
            logging.error(f"计算MD5失败: {file_path}, 错误: {e}")
            return None

    def _save_command(self, command):
        # 将指令追加到文件
        try:
            commands = []
            if os.path.exists(Config.COMMAND_FILE):
                with open(Config.COMMAND_FILE, 'r') as f:
                    commands = json.load(f)
            commands.append(command)
            with open(Config.COMMAND_FILE, 'w') as f:
                json.dump(commands, f, indent=2)
        except Exception as e:
            logging.error(f"保存指令失败: {e}")

# API服务
app = Flask(__name__)

@app.route('/api/commands', methods=['GET'])
def get_commands():
    # 验证请求
    auth_header = request.headers.get('Authorization')
    if auth_header != f'Bearer {Config.AUTH_KEY}':
        return jsonify({'error': '认证失败'}), 401
    
    # 返回待执行指令
    try:
        if not os.path.exists(Config.COMMAND_FILE):
            return jsonify([])
        with open(Config.COMMAND_FILE, 'r') as f:
            commands = json.load(f)
        return jsonify(commands)
    except Exception as e:
        logging.error(f"获取指令失败: {e}")
        return jsonify({'error': '服务器错误'}), 500

@app.route('/api/commands/clear', methods=['POST'])
def clear_commands():
    # 验证请求
    auth_header = request.headers.get('Authorization')
    if auth_header != f'Bearer {Config.AUTH_KEY}':
        return jsonify({'error': '认证失败'}), 401
    
    # 清除已执行指令
    try:
        executed_ids = request.json.get('executed_ids', [])
        if not os.path.exists(Config.COMMAND_FILE):
            return jsonify({'status': 'success'})
        
        with open(Config.COMMAND_FILE, 'r') as f:
            commands = json.load(f)
        
        # 过滤已执行的指令
        remaining_commands = [cmd for i, cmd in enumerate(commands) 
                             if i not in executed_ids]
        
        with open(Config.COMMAND_FILE, 'w') as f:
            json.dump(remaining_commands, f, indent=2)
        
        return jsonify({'status': 'success'})
    except Exception as e:
        logging.error(f"清除指令失败: {e}")
        return jsonify({'error': '服务器错误'}), 500

# 启动监控和API服务
def start_file_watcher():
    event_handler = BtConfigHandler()
    observer = Observer()
    for directory in Config.WATCH_DIRS:
        if os.path.exists(directory):
            observer.schedule(event_handler, directory, recursive=True)
            logging.info(f"监控目录: {directory}")
        else:
            logging.warning(f"目录不存在，跳过监控: {directory}")
    observer.start()
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        observer.stop()
    observer.join()

def start_api_server():
    logging.info(f"API服务启动，端口: {Config.API_PORT}")
    app.run(host='0.0.0.0', port=Config.API_PORT)

if __name__ == '__main__':
    logging.info("启动宝塔配置同步主服务器...")
    
    # 启动文件监控线程
    watcher_thread = threading.Thread(target=start_file_watcher)
    watcher_thread.daemon = True
    watcher_thread.start()
    
    # 启动API服务
    start_api_server()    